package  
{
	import Box2D.Collision.b2AABB;
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Collision.Shapes.b2Shape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2Fixture;
	import Box2D.Dynamics.b2FixtureDef;
	import Box2D.Dynamics.b2World;
	import Box2D.Dynamics.Joints.b2MouseJoint;
	import Box2D.Dynamics.Joints.b2MouseJointDef;
	import com.greensock.easing.Elastic;
	import com.greensock.easing.Sine;
	import com.greensock.TweenLite;
	import com.quasimondo.tools.MarchingAntsRect;
	import fileInterface.FileInterface;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.DropShadowFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.SharedObject;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;
	import SWFKit.Application;
	import SWFKit.Dialogs;
	import SWFKit.File;
	import SWFKit.Global;
	import SWFKit.Image;
	import SWFKit.Shell;
	/**
	 * ...
	 * @author GertS
	 */
	public class SnippingTool extends MovieClip
	{
		
		
		public var background:MovieClip;
		public var marchingAnts:MarchingAntsRect;
		public var marchingAntsHolder:Sprite;
		public var crosshair:MovieClip;
		public var label_txt:TextField;
		public var infoPnl:MovieClip;
		public var actionsPnl:MovieClip;
		private var _selectionRect:Rectangle;
		private var _spacebarDownDistanceX:int;
		private var _spacebarDownDistanceY:int;
		private var _image:Image;
		private var _path:String;
		static public var _world:b2World;
		public var _velocityIte_pixelsInAMeterns:int = 10;
		public var _positionIte_pixelsInAMeterns:int = 10;
		public var _timeStep:Number = 1.0/30.0;
		public var _pixelsInAMeter:Number = 30;
		private var _imgLoader:Loader;
		public var lbl_txt:TextField;
		static public var box2DHolder:Sprite;
		private var _isMouseDown:Boolean = false;
		private var _so:SharedObject;
		public var mcMagnifier:MovieClip;
		
		public var m_mouseJoint:b2MouseJoint;
		// world mouse position
		static public var mouseXWorldPhys:Number;
		static public var mouseYWorldPhys:Number;
		static public var mouseXWorld:Number;
		static public var mouseYWorld:Number;
		
		private var mousePVec:b2Vec2 = new b2Vec2();
		private var _isBodyDragging:Boolean;
		//private var _deleteImagePanel:DeleteImagePanel;
		private var _currentBody:b2Body;
		//private var _isBodyOverDeleteImagePanel:Boolean;
		public static var imageList:Array;
		private var _fileInterface:FileInterface;
		
		static public var settings:XML;
		
		
		public function SnippingTool() 
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			
			var settingsLoader:URLLoader = new URLLoader(new URLRequest("assets/settings.xml"));
			settingsLoader.addEventListener(Event.COMPLETE, onSettingsComplete);
			
			label_txt.visible = false;
			stage.addEventListener(Event.RESIZE, onResize);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressedDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyPressedUp);
			onResize();
			//Mouse.hide();
			actionsPnl.exit_btn.addEventListener(MouseEvent.CLICK, onExitClicked, false, 100);
			actionsPnl.savePath_btn.addEventListener(MouseEvent.CLICK, onSavePathClicked, false, 100);
			
			marchingAntsHolder = new Sprite();
			this.addChild(marchingAntsHolder);
			marchingAnts = new MarchingAntsRect();
			marchingAntsHolder.addChild(marchingAnts);
			
			_so = SharedObject.getLocal("net.stogo.snippingTool");
			if (_so.data.path) {
				_path = _so.data.path;
			} else {
				try {
					_path = new Shell().getSpecialFolder("my documents");
				} catch (e:Error) {
					
				}
			}
			
			setTimeout(setup2D, 10);
			
			box2DHolder = new Sprite();
			//this.addChildAt(box2DHolder, 0);
			this.addChild(box2DHolder);
			
			imageList = new Array();
			
		}
		
		private function onSettingsComplete(e:Event):void 
		{
			settings = new XML(e.target.data)
			_fileInterface = new FileInterface();
			addChild(_fileInterface);
			_fileInterface.settingsXML = settings;
		}

		private function setup2D():void
		{
			//http://www.kerp.net/box2d/displaylesson.php?chapter=1
			
			var worldAABB:b2AABB = new b2AABB();
			//worldAABB.lowerBound.Set(-1000.0, -1000.0);
			//worldAABB.upperBound.Set(1000.0, 1000.0);
			
			// Define the gravity vector
			var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
			
			// Allow bodies to sleep
			var doSleep:Boolean = true;
			
			// Construct a world object
			_world = _world = new b2World(gravity, doSleep);
			_world.SetWarmStarting(true);
			// set debug draw
			var dbgDraw:b2DebugDraw = new b2DebugDraw();
			dbgDraw.SetSprite(box2DHolder);
			dbgDraw.SetDrawScale(_pixelsInAMeter);
			dbgDraw.SetFillAlpha(0.3);
			dbgDraw.SetLineThickness(1.0);
			dbgDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
			_world.SetDebugDraw(dbgDraw);
			
			// Create border of boxes
			var wall:b2PolygonShape= new b2PolygonShape();
			var wallBd:b2BodyDef = new b2BodyDef();
			var wallB:b2Body;
			
			// Left
			wallBd.position.Set( -50 / _pixelsInAMeter, (stage.stageHeight / 2 ) / _pixelsInAMeter) ;
			wall.SetAsBox(50 /_pixelsInAMeter, (stage.stageHeight / 2) / _pixelsInAMeter);
			wallB = _world.CreateBody(wallBd);
			wallB.CreateFixture2(wall);
			// Right
			wallBd.position.Set( (stage.stageWidth + 50) / _pixelsInAMeter, (stage.stageHeight / 2 ) / _pixelsInAMeter) ;
			wallB = _world.CreateBody(wallBd);
			wallB.CreateFixture2(wall);
			// Top
			wallBd.position.Set((stage.stageWidth / 2)  / _pixelsInAMeter , -50 / _pixelsInAMeter);
			wall.SetAsBox((stage.stageWidth / 2) /_pixelsInAMeter, 50 / _pixelsInAMeter);
			wallB = _world.CreateBody(wallBd);
			wallB.CreateFixture2(wall);
			// Bottom
			wallBd.position.Set((stage.stageWidth / 2)  / _pixelsInAMeter , (stage.stageHeight + 50 ) / _pixelsInAMeter);
			wallB = _world.CreateBody(wallBd);
			wallB.CreateFixture2(wall);
			
			// Add bodies
			var fd:b2FixtureDef = new b2FixtureDef();
			var sd:b2PolygonShape = new b2PolygonShape();
			var bd:b2BodyDef = new b2BodyDef();
			bd.type = b2Body.b2_dynamicBody;
			var b:b2Body;
			fd.density = 1.0;
			fd.friction = 0.5;
			fd.restitution = 0.1;
			fd.shape = sd;
			
			stage.addEventListener(Event.ENTER_FRAME, update2D, false, 0, true);
			
		}
		
		
		public function update2D(e:Event):void {
		
			UpdateMouseWorld();
			MouseDrag();
			
			// Update physics
			var physStart:uint = getTimer();
			_world.Step(_timeStep, _velocityIte_pixelsInAMeterns, _positionIte_pixelsInAMeterns);
			_world.ClearForces();
			
			// Render
			//_world.DrawDebugData();
			
			for (var bb:b2Body = _world.GetBodyList(); bb; bb = bb.GetNext()) {
				if (bb.IsAwake()) {
					if (bb.GetUserData() is Sprite) {
						var spr:Sprite = bb.GetUserData();
						spr.x = bb.GetPosition().x * _pixelsInAMeter;
						spr.y = bb.GetPosition().y * _pixelsInAMeter;
						spr.rotation = bb.GetAngle() * 180 / Math.PI;
					}
				}
			}
			
		}
		
		private function onSavePathClicked(e:MouseEvent):void 
		{
			var path = new Dialogs().browse("Choose a destination folder for all captured images", _path);
			if (path) {
				_path = path;
				_so.data.path = _path;
			}
			trace(_so.data.path);
		}
		
		private function saveFile():void 
		{
			if (_image) {
				var d:Date = new Date();
				var fn:String = "capture ";
				fn += d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate() + " " + d.getHours() + "-" + d.getMinutes() + "-" + d.getSeconds();
				fn += ".jpg";
				fn = _path + "\\" + fn;
				var compression:int = 100; //(0 - 100?)
				if (!_image.save(fn, compression)) {
					var dlg:Dialogs = new Dialogs;
					dlg.msgBox("Could not save the file " + fn, "Sorry", 48)
				} else {
					setTimeout(drawImage, 10, fn);
				}
			}
		}
		
		private function drawImage(fn:String):void
		{
			_imgLoader = new Loader();
			//this.addChild(_imgLoader);
			_imgLoader.x = Math.min(_selectionRect.left, _selectionRect.right);
			_imgLoader.y = Math.min(_selectionRect.top, _selectionRect.bottom);
			_imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoadDone);
			imageList.push(fn);
			
			_imgLoader.load(new URLRequest(fn));
		}
		
		private function imgLoadDone(e:Event):void 
		{
			_imgLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, imgLoadDone);
			
			//create box2B object
			var img:b2PolygonShape= new b2PolygonShape();
			var imgBd:b2BodyDef = new b2BodyDef();
			var imgB:b2Body;
			var fd:b2FixtureDef = new b2FixtureDef();
			
			var spr:Sprite = new Sprite();
			spr.name = "img" + String(imageList.length - 1);
			spr.alpha = 0;
			var bitmap:Bitmap = new Bitmap(e.target.content.bitmapData, "auto", true);
			bitmap.x = - _imgLoader.width / 2;
			bitmap.y = - _imgLoader.height / 2;
			
			var minScale:Number = 150 / Math.max(_imgLoader.width, _imgLoader.height);
			var scale:Number = Math.min(1, minScale);
			var newWidth:Number = bitmap.width * scale;
			var newHeight:Number = bitmap.height * scale;
			TweenLite.to(spr, 1, { alpha:1, scaleX: scale, scaleY:scale, ease:Sine.easeInOut} );
			spr.addChild(bitmap);
			box2DHolder.addChild(spr);
			
			var dropShadow:DropShadowFilter = new DropShadowFilter();
			dropShadow.distance = 0;
			dropShadow.angle = 45;
			dropShadow.color = 0x333333;
			dropShadow.alpha = 1;
			dropShadow.blurX = 5;
			dropShadow.blurY = 5;
			dropShadow.strength = 1;
			dropShadow.inner = false;
			dropShadow.knockout = false;
			dropShadow.hideObject = false;
			dropShadow.quality =  BitmapFilterQuality.LOW;
			spr.filters = new Array(dropShadow);
            
			imgBd.position.Set( (_imgLoader.x + (bitmap.width / 2)) / _pixelsInAMeter, (_imgLoader.y + (bitmap.height / 2)) / _pixelsInAMeter);
			imgBd.angle = (Math.random() - 1) * 0.01;
			imgBd.type = b2Body.b2_dynamicBody;
			imgBd.userData = spr;
			img.SetAsBox((newWidth / 2) / _pixelsInAMeter, (newHeight / 2) / _pixelsInAMeter);
			//lbl_txt.text = (_imgLoader.width + "/" + _imgLoader.height)
			
			imgB = _world.CreateBody(imgBd);
			fd.density = 2.0;
			fd.friction = 0.5;
			fd.restitution = 0.2;
			fd.shape = img;
			imgB.CreateFixture(fd);
		}
		
		private function onExitClicked(e:MouseEvent):void 
		{
			var mw = Global.getMainWnd();
			mw.close();
		}

		private function keyPressedDown(event:KeyboardEvent):void {
		  var key:uint = event.keyCode;
		  switch (key) {
				case Keyboard.SPACE : 
					_spacebarDownDistanceX = _selectionRect.width;
					_spacebarDownDistanceY = _selectionRect.height;
					break;
				}
		}
		
		private function keyPressedUp(event:KeyboardEvent):void {
		  var key:uint = event.keyCode;
		  switch (key) {
			  case Keyboard.SPACE :
				_spacebarDownDistanceX = NaN;
				_spacebarDownDistanceY = NaN;
				break;
		  }
		}
		
		private function onResize(e:Event = null):void 
		{
			background.width = stage.stageWidth;
			background.height = stage.stageHeight;
			infoPnl.x = stage.stageWidth / 2;
			actionsPnl.x = stage.stageWidth;
		}
		
		private function onMouseMove(e:Event = null):void 
		{
			if (actionsPnl.hitTestPoint(mouseX, mouseY) || actionsPnl.hitTestPoint(mouseX, mouseY)) {
				return;
			}
			
			
			if (_isBodyDragging) {
				/*
				if (_deleteImagePanel) {
					if (_deleteImagePanel.hitTestPoint(mouseX, mouseY)) {
						if (!_isBodyOverDeleteImagePanel) {
							onRolloverDeleteImagePanel();
							_isBodyOverDeleteImagePanel = true;
						}
					} else {
						if (_isBodyOverDeleteImagePanel) {
							_isBodyOverDeleteImagePanel = false;
							onRolloutDeleteImagePanel();
						}
					}
				}
				*/
				return
			}
			
			
			if (_isMouseDown) {
				if (_spacebarDownDistanceX) {
					_selectionRect.x = mouseX - _spacebarDownDistanceX;
					_selectionRect.y = mouseY - _spacebarDownDistanceY;
				} else {
					_selectionRect.bottomRight = new Point(mouseX, mouseY);
				}
				marchingAnts.draw(_selectionRect);
				marchingAnts.visible = true;
				drawLabel();
				//drawMagnifier();
			} else {
				//marchingAnts.;
			}
		}
		
		private function drawMagnifier():void
		{
			var bitmapData:BitmapData = new BitmapData(30, 30, false);
			var rect:Rectangle = new Rectangle(mouseX - 15, mouseY - 15, 30, 30);
			bitmapData.draw(stage, null, null, null, rect);
			
			
			if (mcMagnifier.mcHolder.numChildren > 0) {
				mcMagnifier.mcHolder.removeChildAt(0);
			}
            mcMagnifier.mcHolder.addChild(new Bitmap(bitmapData));
            
			
		}
		
		private function captureScreen():void
		{
			//l,t,r,b
			_image = Image.captureScreen(	Math.min(_selectionRect.left, _selectionRect.right), 
											Math.min(_selectionRect.top, _selectionRect.bottom),
											Math.max(_selectionRect.left, _selectionRect.right), 
											Math.max(_selectionRect.top, _selectionRect.bottom));
			if (_image) {
				_image.copy();//send to clipboard
				saveFile();
			} else {
				var dlg:Dialogs = new Dialogs;
				dlg.msgBox("Could not copy to the clipboard", "Sorry", 48)
			}
			
			//label_txt.visible = true;
			infoPnl.visible = true;
			actionsPnl.visible = true;
			//marchingAnts.visible = true;
			
		}
		
		private function drawLabel():void
		{
			label_txt.visible = true;
			label_txt.text = Math.abs(_selectionRect.width) + " x " + Math.abs(_selectionRect.height);
			label_txt.x = _selectionRect.x + ((_selectionRect.width - label_txt.width) / 2);
			if (Math.abs(_selectionRect.height) > 30  && Math.abs(_selectionRect.width) > 40) {
				label_txt.y = Math.min(_selectionRect.top, _selectionRect.bottom) + ((Math.abs(_selectionRect.height) - label_txt.textHeight) / 2);
			} else {
				label_txt.y = Math.min(_selectionRect.top, _selectionRect.bottom) - label_txt.textHeight - 3;
			}
			
		}
		
		private function onMouseDown(e:MouseEvent = null):void 
		{
			_isMouseDown = true;
			_selectionRect = new Rectangle(mouseX, mouseY, 0, 0);
					
			infoPnl.info_txt.text = "Drag and hold to snip. Use SPACE to reposition."
			//showFileInterface();	//temp!!
			_currentBody = GetBodyAtMouse();
			if (_currentBody) {
				_isBodyDragging = true;
				showFileInterface();	
			} else {
				TweenLite.to(box2DHolder, 0.4, {delay:1, alpha:0} );
			}
		}
		
		private function onMouseUp(e:MouseEvent):void 
		{
			_isMouseDown = false;
						
			label_txt.visible = false;
			if (infoPnl.getRect(this).intersects(_selectionRect)) {
				infoPnl.visible = false;
			}
			if (actionsPnl.getRect(this).intersects(_selectionRect)) {
				actionsPnl.visible = false;
			}
			marchingAnts.visible = false;
			
			if (Math.abs(_selectionRect.width) > 0 && Math.abs(_selectionRect.height) > 0) {
				var w:uint = setTimeout(captureScreen, 50);
			} 
			
			_isBodyDragging = false;
			
			//hideFileInterface();
			
			TweenLite.to(box2DHolder, 0.4, {delay:0.4, alpha:1} );
			
		}
		
		
		//======================
		// GetBodyAtMouse
		//======================
		
		public function GetBodyAtMouse(includeStatic:Boolean = false):b2Body {
			// Make a small box.
			mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
			aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
			var body:b2Body = null;
			var fixture:b2Fixture;
			
			// Query the world for overlapping shapes.
			function GetBodyCallback(fixture:b2Fixture):Boolean
			{
				var shape:b2Shape = fixture.GetShape();
				if (fixture.GetBody().GetType() != b2Body.b2_staticBody || includeStatic)
				{
					var inside:Boolean = shape.TestPoint(fixture.GetBody().GetTransform(), mousePVec);
					if (inside)
					{
						body = fixture.GetBody();
						return false;
					}
				}
				return true;
			}
			_world.QueryAABB(GetBodyCallback, aabb);
			return body;
		}
		
		public function MouseDrag():Boolean{
			// mouse press
			if (_isMouseDown && !m_mouseJoint){
				
				var body:b2Body = GetBodyAtMouse();
				
				if (body)
				{
					var md:b2MouseJointDef = new b2MouseJointDef();
					md.bodyA = _world.GetGroundBody();
					md.bodyB = body;
					md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
					md.collideConnected = true;
					md.maxForce = 300.0 * body.GetMass();
					m_mouseJoint = _world.CreateJoint(md) as b2MouseJoint;
					body.SetAwake(true);
				}
			}
			
			
			// mouse release
			if (!_isMouseDown){
				if (m_mouseJoint)
				{
					_world.DestroyJoint(m_mouseJoint);
					m_mouseJoint = null;
				}
			}
			
			
			// mouse move
			if (m_mouseJoint)
			{
				var p2:b2Vec2 = new b2Vec2(mouseXWorldPhys, mouseYWorldPhys);
				m_mouseJoint.SetTarget(p2);
			}
			return (body == null);
		}
		
		public function UpdateMouseWorld():void{
			mouseXWorldPhys = (mouseX)/_pixelsInAMeter; 
			mouseYWorldPhys = (mouseY)/_pixelsInAMeter; 
			
			mouseXWorld = (mouseX); 
			mouseYWorld = (mouseY); 
		}
		
		private function showFileInterface():void
		{
			_fileInterface.attachedBody = _currentBody;
		}
		
		/*
		private function onRolloverDeleteImagePanel():void 
		{
			//e.currentTarget.alpha = 1;
			if (_currentBody.GetUserData() is Sprite) {
				var spr:Sprite = _currentBody.GetUserData();
				TweenLite.to(spr, 0.4, { alpha:0.2 } );
				TweenLite.to(_deleteImagePanel._btn, 1, { scaleX:1.2, scaleY:1.2 , ease:Elastic.easeOut} );
			}
		}
		
		private function onRolloutDeleteImagePanel():void 
		{
			if (_currentBody.GetUserData() is Sprite) {
				var spr:Sprite = _currentBody.GetUserData();
				TweenLite.to(spr, 0.4, { alpha:1 } );
				TweenLite.to(_deleteImagePanel._btn, 1, { scaleX:1, scaleY:1 ,ease:Elastic.easeOut} );
			}
		}
		
		private function hideFileInterface():void
		{
			if (_deleteImagePanel) {
				var delay:Number = 0.5;
				if (_isBodyOverDeleteImagePanel) {
					var spr:Sprite = Sprite(_currentBody.GetUserData());
					TweenLite.to(spr, 0.6, { scaleX:0, scaleY:0, x:stage.width, y:_deleteImagePanel.y, alpha:0, onComplete:onBodyReadyToBeDeleted } );
					_currentBody.SetActive(false);
					delay += 0.6;
					TweenLite.to(_deleteImagePanel._btn, 1, { scaleX:0.5, scaleY:0.5, ease:Elastic.easeOut, delay:0.4 } );
				} 
				TweenLite.to(_deleteImagePanel, 0.4, { delay:delay, alpha:0, x:_deleteImagePanel.x + 100, onComplete:hideDeleteImagePanel } );
			}
		}
	
		private function hideDeleteImagePanel():void
		{
			removeChild(_deleteImagePanel);
			_deleteImagePanel = null;
		}
		*/
		
		private function onOpenFolder():void {
			new Shell().open("c:\\");
		}
		
		public function get path():String { return _path; }
		
		/*
		private function onBodyReadyToBeDeleted():void
		{
			var spr:Sprite = Sprite(_currentBody.GetUserData());
			var fn:String = _imageList[spr.name.split("img").join("")];
			var file:File = new File(fn);
			//infoPnl.info_txt.text = fn;
			
			file.remove();
			box2DHolder.removeChild(spr);
			_world.DestroyBody(_currentBody);
		}
		*/
	}
	
}